package com.soundslikeobiwan.ontocalendar;

//import java.lang.*;
import java.util.*;
import java.io.ByteArrayOutputStream;

import javax.microedition.pim.*;
import javax.wireless.messaging.*;

import net.rim.blackberry.api.pdap.*;
import net.rim.blackberry.api.invoke.*;
import net.rim.blackberry.api.mail.*;
import net.rim.blackberry.api.menuitem.ApplicationMenuItem;
import net.rim.blackberry.api.menuitem.ApplicationMenuItemRepository;
import net.rim.blackberry.api.options.OptionsManager;
import net.rim.blackberry.api.options.OptionsProvider;
import net.rim.device.api.system.PersistentObject;
import net.rim.device.api.system.PersistentStore;
import net.rim.device.api.ui.component.Dialog;
import net.rim.device.api.ui.component.NumericChoiceField;
import net.rim.device.api.ui.component.ObjectChoiceField;
import net.rim.device.api.ui.component.Status;
import net.rim.device.api.ui.container.MainScreen;
import net.rim.device.api.util.Persistable;
import net.rim.blackberry.api.mail.event.*;
import net.rim.device.api.system.Clipboard;

class ClipboardLogger {
    private static Clipboard _cb;
    
    public ClipboardLogger() {
        _cb = Clipboard.getClipboard();

    }
    
    public static void print(String s) {
        if (null == s) {
            _cb.put(null);
        } else {
            String was = _cb.toString();
            if (null == was) was = "";
            _cb.put(was + s);
        }
    }
    public static void println(String s) {
        if (null == s) {
            _cb.put(null);
        } else {
            String was = _cb.toString();
            if (null == was) was = "";
            _cb.put(was + s + "\n\r") ;
        }
    }
}

/*
 * OntoCalendar application
 * This registers a simple menu item with the system to be shown
 * when viewing an email or SMS Message. The run method for this menuItem
 * simply gets the message for use
 *
 * Copyright (C) 2003 Research In Motion Ltd.
 */
class OntoCalendarOptions implements OptionsProvider {
    // members
    private ObjectChoiceField _getNote;
    private ObjectChoiceField _disableCalendar;
    private ObjectChoiceField _disableTasks;
    private ObjectChoiceField _disableInvite;
    private ObjectChoiceField _makePrivate;
    
    private NumericChoiceField _duration;
    private NumericChoiceField _reminder;
    private NumericChoiceField _taskDue;
    private ObjectChoiceField  _taskReminder;
    
    private OntoCalendarData _data;
    private String _title;
    private static OntoCalendarOptions _instance;
    
    // constructors
    private OntoCalendarOptions()   {
    }
    private OntoCalendarOptions(String title)       {
        _title = title;
        _data = OntoCalendarData.load();
    }
    // Only allow one instance of this class.
    public static OntoCalendarOptions getInstance() {
        if (_instance == null) {
            _instance = new OntoCalendarOptions("OntoCalendar v3.6.1");
        }
        return _instance;
    }
    private static int getCurOpts() {
            
        int curopts = getInstance().getData().getSelected();
        return curopts;
    }
    public static int getDoCopy() {
        int doCopy = getCurOpts() & 1;
        return doCopy;
    }
    public static int getDurationIndex() {
        int durationIndex = (getCurOpts() & 0x1e) >> 1;    /* 4 bits, increments of 15 minutes */
        return durationIndex;
    }
    public static int getReminderIndex() {
        int reminderIndex = (getCurOpts() & 0x1e0) >> 5;    /* 4 bits, increments of 15 minutes */
        return reminderIndex;
    }
    public static int getTaskDueIndex() {
        int taskDueIndex = (getCurOpts() & 0x1e00) >> 9;    /* 4 bits, increments of 1 Day */
        return taskDueIndex;
    }

    public static int getCalendarDisabled() {               /* hide "Onto Calendar..." menu item */
        return 0;
        //return getCalendarDisabled(getCurOpts());
    }
    public static int getCalendarDisabled(int val ) {
        int calendarDisabled = (val & 0x2000) >> 13;
        return calendarDisabled;
    }

    public static int getTasksDisabled() {
        return 0;
        //return getTasksDisabled(getCurOpts());
    }
    public static int getTasksDisabled(int val) {                  /* hide "Onto Tasks..." menu item */
        int tasksDisabled = (val & 0x4000) >> 14;
        return tasksDisabled;
    }

    public static int getInvitesDisabled() {                /* hide "Invite Sender..." menu item */
        return 0;
        //return getInvitesDisabled(getCurOpts());
    }
    public static int getInvitesDisabled(int val) {
        int invitesDisabled = (val & 0x8000) >> 15;
        return invitesDisabled;
    }
    public static int getMakePrivate() {                /* hide "Invite Sender..." menu item */
        return getMakePrivate(getCurOpts());
    }
    public static int getMakePrivate(int val) {
        int makePrivate = (val & 0x10000) >> 16;
        return makePrivate;
    }

    // Save the data.
    public void save()      {
        int prev = getCurOpts();
        _data.setSelected(_getNote.getSelectedIndex() + 
                    (_duration.getSelectedIndex() <<1 ) + 
                    (_reminder.getSelectedIndex() << 5) +
                    (_taskDue.getSelectedIndex() << 9) +
                    (_makePrivate.getSelectedIndex() << 16)
                    );
        _data.commit();
        /* see if we have to hide or show any menu items */
        /* TODO: enable or disable menu items based on changes to the ...Disabled() options */
        
    }    // On startup, create the instance and register it.
    public static void Register()       {
        OptionsManager.registerOptionsProvider(getInstance());
    }
    // Get the title for the option item.
    public String getTitle() {
        return _title;
    }
    // Add fields to the screen.
    public void populateMainScreen(MainScreen screen) {
        int doCopy = getDoCopy();
        int doMakePrivate = getMakePrivate();
        //int disableCalendar = getCalendarDisabled();
        //int disableTasks = getTasksDisabled();
        //int disableInvite = getInvitesDisabled();
        int durationIndex = getDurationIndex();    /* increments of 15 minutes */
        int reminderIndex = getReminderIndex();    /* increments of 15 minutes */
        int taskDueIndex = getTaskDueIndex();    /* increments of 1 Day */
        String[] choices = {"No", "Yes"};                                       

        //_disableCalendar = new ObjectChoiceField("Hide OntoCalendar:", choices, disableCalendar);
        //screen.add(_disableCalendar);
        //_disableTasks = new ObjectChoiceField("Hide OntoTasks:", choices, disableTasks);
        //screen.add(_disableTasks);
        //_disableInvite = new ObjectChoiceField("Hide InviteSender:", choices, disableInvite);
        //screen.add(_disableInvite);
        _getNote = new ObjectChoiceField("Copy Message: ", choices, doCopy);
        screen.add(_getNote);
        _makePrivate = new ObjectChoiceField("Make Events Private: ", choices, doMakePrivate);
        screen.add(_makePrivate);        
        _duration = new NumericChoiceField ("Default Duration: ", 0, 256, 15, durationIndex);
        screen.add(_duration);
        _reminder = new NumericChoiceField ("Default Reminder: ", 0, 256, 15, reminderIndex);
        screen.add(_reminder);
        _taskDue = new NumericChoiceField ("Task Due in (Days): ", 0, 60, 1, taskDueIndex);
        screen.add(_taskDue);

    } 

    // Retrieve the data. Used by other applications to access options data.
    public OntoCalendarData getData() {
        return _data;
    }
    // Inner class to store selected option values.
    public static final class OntoCalendarData implements Persistable        {
        private static final long ID = 0x4242477421000001L;
        private int _selectedOption;
        private OntoCalendarData() {
        }
        public int getSelected() {
            return _selectedOption;
        }
        public void setSelected(int index) {
            _selectedOption = index;        
        }
        public void commit() {
            PersistentObject.commit(this);
        }
        private static OntoCalendarData load() {
            PersistentObject persist = PersistentStore.getPersistentObject( OntoCalendarData.ID );
            OntoCalendarData contents = (OntoCalendarData)persist.getContents();
            synchronized( persist ) {
                if( contents == null ) {
                    contents = new OntoCalendarData();
                    persist.setContents( contents );
                    persist.commit();
                }
            }
            return contents;
        }
    }   
}

class OntoCalendar  {
    // A simple tool to put the "current" SMS or Email message into a new calendar or task item

    public static void main(String[] args){
        System.out.println("Starting OntoCalendar");
        (new EventMenuItem(0)).registerInstance();
        (new TaskMenuItem(0)).registerInstance();
        (new MeetingMenuItem(0)).registerInstance();
        OntoCalendarOptions.Register();

    }
}

class EventMenuItem extends ApplicationMenuItem
{

    // we'll use the default constructors here.
    EventMenuItem(int order){
        super(order);
    }

    // Register the instance of the menuItem with the system
    public void registerInstance(){
        if (0 == OntoCalendarOptions.getCalendarDisabled()) {
            ApplicationMenuItemRepository.getInstance().removeMenuItem(ApplicationMenuItemRepository.MENUITEM_PHONELOG_VIEW,this);
            ApplicationMenuItemRepository.getInstance().addMenuItem(ApplicationMenuItemRepository.MENUITEM_PHONELOG_VIEW,this);

            ApplicationMenuItemRepository.getInstance().removeMenuItem(ApplicationMenuItemRepository.MENUITEM_PHONE,this);
            ApplicationMenuItemRepository.getInstance().addMenuItem(ApplicationMenuItemRepository.MENUITEM_PHONE,this);

            ApplicationMenuItemRepository.getInstance().removeMenuItem(ApplicationMenuItemRepository.MENUITEM_EMAIL_VIEW,this);
            ApplicationMenuItemRepository.getInstance().addMenuItem(ApplicationMenuItemRepository.MENUITEM_EMAIL_VIEW,this);
            
            ApplicationMenuItemRepository.getInstance().removeMenuItem(ApplicationMenuItemRepository.MENUITEM_SMS_VIEW,this);
            ApplicationMenuItemRepository.getInstance().addMenuItem(ApplicationMenuItemRepository.MENUITEM_SMS_VIEW,this);
            
            ApplicationMenuItemRepository.getInstance().removeMenuItem(ApplicationMenuItemRepository.MENUITEM_MESSAGE_LIST,this);
            ApplicationMenuItemRepository.getInstance().addMenuItem(ApplicationMenuItemRepository.MENUITEM_MESSAGE_LIST,this);

            ApplicationMenuItemRepository.getInstance().removeMenuItem(ApplicationMenuItemRepository.MENUITEM_ADDRESSBOOK_LIST,this);
            ApplicationMenuItemRepository.getInstance().addMenuItem(ApplicationMenuItemRepository.MENUITEM_ADDRESSBOOK_LIST,this);

            ApplicationMenuItemRepository.getInstance().removeMenuItem(ApplicationMenuItemRepository.MENUITEM_ADDRESSCARD_VIEW,this);
            ApplicationMenuItemRepository.getInstance().addMenuItem(ApplicationMenuItemRepository.MENUITEM_ADDRESSCARD_VIEW,this);
        }
    }
    // Register the instance of the menuItem with the system
    public void unRegisterInstance(){

        ApplicationMenuItemRepository.getInstance().removeMenuItem(ApplicationMenuItemRepository.MENUITEM_PHONELOG_VIEW,this);
        ApplicationMenuItemRepository.getInstance().removeMenuItem(ApplicationMenuItemRepository.MENUITEM_EMAIL_VIEW,this);
        ApplicationMenuItemRepository.getInstance().removeMenuItem(ApplicationMenuItemRepository.MENUITEM_SMS_VIEW,this);
        ApplicationMenuItemRepository.getInstance().removeMenuItem(ApplicationMenuItemRepository.MENUITEM_MESSAGE_LIST,this);
        ApplicationMenuItemRepository.getInstance().addMenuItem(ApplicationMenuItemRepository.MENUITEM_MESSAGE_LIST,this);
    }

    //methods we must implement

    //toString should return the string we want to
    //use as the lable of the menuItem
    public String toString(){
        return "Onto Calendar...";
    }
/**
 * Returns the name to be displayed for a given Contact
 *
 * @param contact The Contact for which to extract the display name
 * @return The name to be displayed in the list field
 */
public static String getDisplayName(Contact contact) {
    if (contact == null) {
        return null;    }

    String displayName = null;

    // First, see if there is a meaningful name set for the contact.
    if (contact.countValues(Contact.NAME) > 0) {
        final String[] name = contact.getStringArray(Contact.NAME, 0);
        final String firstName = name[Contact.NAME_GIVEN];
        final String lastName = name[Contact.NAME_FAMILY];
        if (firstName != null && lastName != null) {
            displayName = firstName + " " + lastName;
        } else if (firstName != null) {
            displayName = firstName;
        } else if (lastName != null) {
            displayName = lastName;
        }

        if (displayName != null) {
            final String namePrefix = name[Contact.NAME_PREFIX];
            if (namePrefix != null) {
                displayName = namePrefix + " " + displayName;
            }
            return displayName;
        }
    }

    // If not, use the company name.
    if (contact.countValues(Contact.ORG) > 0) {
        final String companyName = contact.getString(Contact.ORG, 0);
        if (companyName != null) {
            return companyName;
        }
    }
    return displayName;
    }

    //Run is called when the menuItem is invokes
    public Object run(Object context) {
                        
        EventList el;
        
        javax.microedition.pim.Event newEvent=null;
        
        int doCopy = OntoCalendarOptions.getDoCopy();
        int makePrivate = OntoCalendarOptions.getMakePrivate();
        int durationIndex = OntoCalendarOptions.getDurationIndex();    /* increments of 15 minutes */
        int reminderIndex = OntoCalendarOptions.getReminderIndex();    /* increments of 15 minutes */
        int alarm = 60 * 15 * reminderIndex;
        
        int period = (durationIndex * 15)* 60*1000;
        // start event on the next period
        long now=System.currentTimeMillis();
        long ceil;
        if (period == 0) {
            ceil = now;
        } else {
            ceil = ( now + period - 1 ) / period * period;
        }
 
        try {
            String msgContent;
            
            int attr = Event.ATTR_NONE;
            int body = Event.NOTE;
            int subject = Event.SUMMARY;
            
            el = (EventList)PIM.getInstance(). openPIMList(PIM.EVENT_LIST, PIM.READ_WRITE);
            newEvent = el.createEvent();
            newEvent.addDate(Event.START, Event.ATTR_NONE, ceil);
            newEvent.addDate(Event.END, Event.ATTR_NONE, period+ceil);
            if (el.isSupportedField(Event.ALARM) && reminderIndex > 0) {
                newEvent.addInt(Event.ALARM, Event.ATTR_NONE, alarm); 
            }
             
            if (null != context) {
                System.out.println("Onto Calendar..." + context.getClass().getName());
            } else {
                System.out.println("Onto Calendar...null context");
            }
            if (context instanceof TextMessage) {
                TextMessage message = (TextMessage) context;
               
                msgContent = message.getPayloadText();
                if (null != msgContent) {
                    newEvent.addString(subject, attr, msgContent);
                }               
                if (doCopy > 0) {
                    msgContent = message.getPayloadText();
                    if (null != msgContent) {
                            newEvent.addString(body, attr, msgContent);
                    }
                }
            } else if (context instanceof net.rim.blackberry.api.blackberrymessenger.Message ) {
                net.rim.blackberry.api.blackberrymessenger.Message  message = (net.rim.blackberry.api.blackberrymessenger.Message ) context;
                msgContent = new String( message.getData());
                if (null != msgContent) {
                    newEvent.addString(subject, attr, msgContent);
                }
            } else if (context instanceof net.rim.blackberry.api.mail.Message) {
                net.rim.blackberry.api.mail.Message message = (net.rim.blackberry.api.mail.Message) context;

                msgContent = message.getSubject();
                if (null != msgContent) {
                    newEvent.addString(subject, attr, msgContent);
                }               
                if (doCopy > 0) {
                    String msgBody = findEmailBody(message.getContent());
                    if (null == msgBody ) {
                        return null;
                    } else {
                        newEvent.addString(body, attr, msgBody);
                    } 
                }
            } else if (context instanceof net.rim.blackberry.api.phone.phonelogs.PhoneCallLog) {
                net.rim.blackberry.api.phone.phonelogs.PhoneCallLog call = (net.rim.blackberry.api.phone.phonelogs.PhoneCallLog)context;
                String name = call.getParticipant().getName();
                if (null == name) name = "";
                String num = call.getParticipant().getAddressBookFormattedNumber();
                
                if ((call.getType() == net.rim.blackberry.api.phone.phonelogs.PhoneCallLog.TYPE_MISSED_CALL_OPENED) 
                    || (call.getType() == net.rim.blackberry.api.phone.phonelogs.PhoneCallLog.TYPE_MISSED_CALL_UNOPENED)) {
                    msgContent = "Missed call from " + name + " (" + num +")";
                } else {
                    msgContent = "Contact " + name + " (" + num + ")";
                }
                if (null != msgContent) {
                    newEvent.addString(subject,attr, msgContent);
                }
                Date date = call.getDate();
                if (date == null)
                    msgContent = "";
                else
                    msgContent = date.toString();
                    
                String notes = call.getNotes();
                if (null != notes) {
                    msgContent = msgContent + "\n\r" + notes;
                }
                newEvent.addString(body,attr,msgContent);
            } else if (context instanceof net.rim.blackberry.api.pdap.BlackBerryContact) {
                net.rim.blackberry.api.pdap.BlackBerryContact contact = (net.rim.blackberry.api.pdap.BlackBerryContact)context;
                PIM pim = PIM.getInstance();
                newEvent.addString(subject,attr, "Contact " + getDisplayName(contact));

                String[] dataFormats = pim.supportedSerialFormats(PIM.CONTACT_LIST);

                ByteArrayOutputStream output = new ByteArrayOutputStream();
                pim.toSerialFormat(contact, output, "UTF8", dataFormats[1]);
                output.close();
                
                newEvent.addString(body,attr,output.toString());
            }

        } catch (Exception ex) {
            Dialog.alert(ex.getClass().getName() + ": " + ex.getMessage());
            return null;
        }
        
        if (makePrivate > 0) {
            newEvent.addInt(Event.CLASS, 0, Event.CLASS_PRIVATE);
        }
        Invoke.invokeApplication(Invoke.APP_TYPE_CALENDAR, new CalendarArguments(CalendarArguments.ARG_NEW, newEvent) );
        return context;
    }
    

            
    //This method reads within an email message to find the email body.
    private String findEmailBody(Object obj)
    {
        ClipboardLogger cb = new ClipboardLogger();
        cb.print(null);
        
        if(obj instanceof Multipart)
        {
            cb.println("findEmailBody: Multipart");
            
            Multipart mp = (Multipart)obj;
            
            //Extract all of the parts within the Multipart message.
            for(int count=0; count < mp.getCount(); ++count)
            {
                cb.println("findEmailBody: " + count + "/" + mp.getCount());
                String msgBody = findEmailBody(mp.getBodyPart(count));
                if (null != msgBody)
                    return msgBody;
            }
            return null;
        }
        else if (obj instanceof TextBodyPart)
        {
            cb.println("findEmailBody: TextBodyPart");
            //This message only has a text body.
            TextBodyPart tbp = (TextBodyPart)obj;
            return readEmailBody(tbp);
        } else if (obj instanceof BodyPart) {
            cb.println("findEmailBody: BodyPart");
            BodyPart bp = (BodyPart)obj;

            if (bp.getContentType().indexOf("text/html") != -1)
            {
                cb.println("findEmailBody: text/html");
                //The message has no attachments.
                //Read the email body, which may contain a TexBodyPart, MimeBodyPart or both.
                return readEmailBody(bp);
            } else if (bp.getContentType().equals("multipart/mixed") || 
                    bp.getContentType().equals("multipart/alternative")) {
                        
                cb.println("findEmailBody: multipart/...");
                //The message has attachments or we are at the top level of the message.  Dig deeper to find the body.
                //Extract all of the parts within the MimeBodyPart message.
                return findEmailBody(bp.getContent());
            }   
        }
        cb.println("findEmailBody: unknown object type " + obj.toString());
        return null;
    }
        
        
    private String readEmailBody(TextBodyPart tbp)
    {
        ClipboardLogger cb = new ClipboardLogger();

        //This is the plain text body.
        String body = (String)tbp.getContent();
        
        //Determine if all of the text body part is present.
        if (tbp.hasMore() && !tbp.moreRequestSent())
        {
            cb.println("readEmailBody(TBP): requesting more");
            //It does, request more of the message.
            try
            {
                Transport.more((BodyPart)tbp, true);
                Status.show("Requesting more of the plain text message body.  Reopen the screen to view it once more has been received.");
                return null;
            }
            catch (Exception ex)
            {
                cb.println("readEmailBody(TBP): exception " + ex.toString());
                Dialog.alert("Exception: " + ex.toString());
                return null;
            }
        }
        cb.println("readEmailBody(TBP): returning ->" + body);
        return body;
    }
   
    //Displays the HTML or plain text body of the email message.
    private String readEmailBody(BodyPart bp)
    {
        ClipboardLogger cb = new ClipboardLogger();
        //Extract the content of the message.
        Object obj = bp.getContent();
        String mimeType = bp.getContentType();
        String body = null;
        
        //Determine if the data returned is a String or a byte array.
        //If the BlackBerry is able to convert the HTML content into a String,
        //then a String should be returned.  If the encoding is not supported a 
        //byte array is returned to allow your application to work with the raw data.
        if (obj instanceof String)
        {
            cb.println("readEmailBody(BP): String");
            body = (String)body;
        }
        else if (obj instanceof byte[])
        {
            cb.println("readEmailBody(BP): byte[]");
            body = new String((byte[])obj);
        }
        
        if (mimeType.indexOf(BodyPart.ContentType.TYPE_TEXT_PLAIN_STRING) != -1)
        {
            cb.println("readEmailBody(BP): text/plain");
            //This is the plain text body.
            
            //Determine if all of the text body part is present.
            if (bp.hasMore() && !bp.moreRequestSent())
            {
                cb.println("readEmailBody(BP): requesting more");
                //It does, request more of the message.
                try
                {
                    Transport.more((BodyPart)bp, true);
                    Status.show("Requesting more of the plain text message body.  Reopen the screen to view it once more has been received.");
                    return null;
                }
                catch (Exception ex)
                {
                    cb.println("readEmailBody(BP): exception " + ex.toString());
                    Dialog.alert("Exception: " + ex.toString());
                    return null;
                }
            }
            cb.println("readEmailBody(BP): returning plain text ->" + body);
            return body;
        }
        else if (mimeType.indexOf("text/html") != -1)
        {
            cb.println("readEmailBody(BP): text/html");
            //Determine if all of the HTML body part is present.
            
            if (bp.hasMore() && !bp.moreRequestSent())
            {
                cb.println("readEmailBody(BP): requesting more");
                //It does, request more of the message.
                try
                {
                    Transport.more((BodyPart)bp, true);
                    Status.show("Requesting more of the HTML message body.  Reopen the screen to view it once more has been received.");
                    return null;
                }
                catch (Exception ex)
                {
                    cb.println("readEmailBody(BP): text/html exception " + ex.toString());                    
                    Dialog.alert("Exception: " + ex.toString());
                    return null;
                }
            } 
            cb.println("readEmailBody(BP): returning text/html ->" + body);
            return body;
        } else {
            cb.println("Unknown MIME type: " + mimeType);
        }
        return null;
    }
}

/* Just like EventMenuItem except invites the sender */
class MeetingMenuItem extends ApplicationMenuItem
{

    // we'll use the default constructors here.
    MeetingMenuItem(int order){
        super(order);
    }

    // Register the instance of the menuItem with the system
    public void registerInstance(){
        if (0 == OntoCalendarOptions.getInvitesDisabled()) {
            ApplicationMenuItemRepository.getInstance().removeMenuItem(ApplicationMenuItemRepository.MENUITEM_EMAIL_VIEW,this);
            ApplicationMenuItemRepository.getInstance().addMenuItem(ApplicationMenuItemRepository.MENUITEM_EMAIL_VIEW,this);
         
            ApplicationMenuItemRepository.getInstance().removeMenuItem(ApplicationMenuItemRepository.MENUITEM_MESSAGE_LIST,this);
            ApplicationMenuItemRepository.getInstance().addMenuItem(ApplicationMenuItemRepository.MENUITEM_MESSAGE_LIST,this);
        }

    }

    //methods we must implement

    //toString should return the string we want to
    //use as the lable of the menuItem
    public String toString(){
        return "Invite Sender...";
    }

    //Run is called when the menuItem is invokes
    public Object run(Object context) {
                        
        EventList el;
        
        javax.microedition.pim.Event newEvent=null;
        
        int doCopy = OntoCalendarOptions.getDoCopy();
        int makePrivate = OntoCalendarOptions.getMakePrivate();

        int durationIndex = OntoCalendarOptions.getDurationIndex();    /* increments of 15 minutes */
        int reminderIndex = OntoCalendarOptions.getReminderIndex();    /* increments of 15 minutes */
        int alarm = 60 * 15 * reminderIndex;
        
        int period = (durationIndex * 15)* 60*1000;
        // start event on the next period
        long now=System.currentTimeMillis();
        long ceil;
        if (period == 0) {
            ceil = now;
        } else {
            ceil = ( now + period - 1 ) / period * period;
        }
 
        try {
            String msgContent;
            
            int attr = Event.ATTR_NONE;
            int body = Event.NOTE;
            int subject = Event.SUMMARY;
            
            if (null != context) {
                System.out.println("Invite Sender..." + context.getClass().getName());
            } else {
                System.out.println("Invite Sender...null context");
            }
            el = (EventList)PIM.getInstance(). openPIMList(PIM.EVENT_LIST, PIM.READ_WRITE);
            newEvent = el.createEvent();
            newEvent.addDate(Event.START, Event.ATTR_NONE, ceil);
            newEvent.addDate(Event.END, Event.ATTR_NONE, period+ceil);
            if (el.isSupportedField(Event.ALARM) && reminderIndex > 0) {
                newEvent.addInt(Event.ALARM, Event.ATTR_NONE, alarm); 
            }

            if (context instanceof net.rim.blackberry.api.mail.Message) {
                net.rim.blackberry.api.mail.Message message = (net.rim.blackberry.api.mail.Message) context;

                msgContent = message.getSubject();
                if (null != msgContent) {
                    newEvent.addString(subject, attr, msgContent);
                }               
                if (doCopy > 0) {
                    String msgBody = findEmailBody(message.getContent());
                    if (null != msgBody)
                        newEvent.addString(body, attr, msgBody);
                    else
                        return null;
                }
                if (el.isSupportedField(BlackBerryEvent.ATTENDEES)) {
                    Address sender = message.getFrom();
                    if (sender != null) {
                        newEvent.addString(BlackBerryEvent.ATTENDEES,attr,sender.getAddr());
                    }
                }
            } 

        } catch (Exception ex) {
            Dialog.alert(ex.getClass().getName() + ": " + ex.getMessage());
            return null;
        }
        if (makePrivate > 0) {
            newEvent.addInt(Event.CLASS, 0, Event.CLASS_PRIVATE);
        }
        Invoke.invokeApplication(Invoke.APP_TYPE_CALENDAR, new CalendarArguments(CalendarArguments.ARG_NEW, newEvent) );
        return context;
    }

    //This method reads within an email message to find the email body.
    private String findEmailBody(Object obj)
    {
        
        if(obj instanceof Multipart)
        {
            Multipart mp = (Multipart)obj;
            
            //Extract all of the parts within the Multipart message.
            for(int count=0; count < mp.getCount(); ++count)
            {
                String msgBody = findEmailBody(mp.getBodyPart(count));
                if (null != msgBody)
                    return msgBody;
            }
            return null;
        }
        else if (obj instanceof TextBodyPart)
        {
            //This message only has a text body.
            TextBodyPart tbp = (TextBodyPart)obj;
            return readEmailBody(tbp);
        } else if (obj instanceof BodyPart) {
            BodyPart bp = (BodyPart)obj;

            if (bp.getContentType().indexOf("text/html") != -1)
            {
                //The message has no attachments.
                //Read the email body, which may contain a TexBodyPart, MimeBodyPart or both.
                return readEmailBody(bp);
            }
            else

               if (bp.getContentType().equals("multipart/mixed") || 
                    bp.getContentType().equals("multipart/alternative"))
            {
                //The message has attachments or we are at the top level of the message.  Dig deeper to find the body.
                //Extract all of the parts within the MimeBodyPart message.
                return findEmailBody(bp.getContent());
            }   
            System.out.println("findEmailBody: unknown MIME type: " + bp.getContentType());
        }           
        return null;
    }
        
        
    private String readEmailBody(TextBodyPart tbp)
    {
        //This is the plain text body.
        String body = (String)tbp.getContent();
        
        //Determine if all of the text body part is present.
        if (tbp.hasMore() && !tbp.moreRequestSent())
        {
            //It does, request more of the message.
            try
            {
                Transport.more((BodyPart)tbp, true);
                Status.show("Requesting more of the plain text message body.  Reopen the screen to view it once more has been received.");
                return null;
            }
            catch (Exception ex)
            {
                Dialog.alert("Exception: " + ex.toString());
                return null;
            }
        }
        return body;
    }
   
    //Displays the HTML or plain text body of the email message.
    private String readEmailBody(BodyPart bp)
    {
        //Extract the content of the message.
        Object obj = bp.getContent();
        String mimeType = bp.getContentType();
        String body = null;
        
        //Determine if the data returned is a String or a byte array.
        //If the BlackBerry is able to convert the HTML content into a String,
        //then a String should be returned.  If the encoding is not supported a 
        //byte array is returned to allow your application to work with the raw data.
        if (obj instanceof String)
        {
            body = (String)obj;
        }
        else if (obj instanceof byte[])
        {
            body = new String((byte[])obj);
        }
        
        if (mimeType.indexOf(BodyPart.ContentType.TYPE_TEXT_PLAIN_STRING) != -1)
        {
            //This is the plain text body.
            
            //Determine if all of the text body part is present.
            if (bp.hasMore() && !bp.moreRequestSent())
            {
                //It does, request more of the message.
                try
                {
                    Transport.more((BodyPart)bp, true);
                    Status.show("Requesting more of the plain text message body.  Reopen the screen to view it once more has been received.");
                    return null;
                }
                catch (Exception ex)
                {
                    Dialog.alert("Exception: " + ex.toString());
                    return null;
                }
            }
            return body;
        }
        else if (mimeType.indexOf("text/html") != -1)
        {
            //Determine if all of the HTML body part is present.
            if (bp.hasMore() && !bp.moreRequestSent())
            {
                //It does, request more of the message.
                try
                {
                    Transport.more((BodyPart)bp, true);
                    Status.show("Requesting more of the HTML message body.  Reopen the screen to view it once more has been received.");
                    return null;
                }
                catch (Exception ex)
                {
                    Dialog.alert("Exception: " + ex.toString());
                    return null;
                }
            }
            return body;
        }
        System.out.println("readEmailBody: unknown MIME type: " + mimeType);
        return null;
    }

}

class TaskMenuItem extends ApplicationMenuItem
{
    // we'll use the default constructors here.
    TaskMenuItem(int order){
        super(order);
    }

    // Register the instance of the menuItem with the system
    public void registerInstance(){
        if (0 == OntoCalendarOptions.getTasksDisabled()) {
            ApplicationMenuItemRepository.getInstance().removeMenuItem(ApplicationMenuItemRepository.MENUITEM_EMAIL_VIEW,this);
            ApplicationMenuItemRepository.getInstance().addMenuItem(ApplicationMenuItemRepository.MENUITEM_EMAIL_VIEW,this);
            
            ApplicationMenuItemRepository.getInstance().removeMenuItem(ApplicationMenuItemRepository.MENUITEM_PHONELOG_VIEW,this);
            ApplicationMenuItemRepository.getInstance().addMenuItem(ApplicationMenuItemRepository.MENUITEM_PHONELOG_VIEW,this);  

            ApplicationMenuItemRepository.getInstance().removeMenuItem(ApplicationMenuItemRepository.MENUITEM_PHONE,this);
            ApplicationMenuItemRepository.getInstance().addMenuItem(ApplicationMenuItemRepository.MENUITEM_PHONE,this);
                      
            ApplicationMenuItemRepository.getInstance().removeMenuItem(ApplicationMenuItemRepository.MENUITEM_SMS_VIEW,this);
            ApplicationMenuItemRepository.getInstance().addMenuItem(ApplicationMenuItemRepository.MENUITEM_SMS_VIEW,this);
            
            ApplicationMenuItemRepository.getInstance().removeMenuItem(ApplicationMenuItemRepository.MENUITEM_MESSAGE_LIST,this);
            ApplicationMenuItemRepository.getInstance().addMenuItem(ApplicationMenuItemRepository.MENUITEM_MESSAGE_LIST,this);
            
            ApplicationMenuItemRepository.getInstance().removeMenuItem(ApplicationMenuItemRepository.MENUITEM_ADDRESSBOOK_LIST,this);
            ApplicationMenuItemRepository.getInstance().addMenuItem(ApplicationMenuItemRepository.MENUITEM_ADDRESSBOOK_LIST,this);

            ApplicationMenuItemRepository.getInstance().removeMenuItem(ApplicationMenuItemRepository.MENUITEM_ADDRESSCARD_VIEW,this);
            ApplicationMenuItemRepository.getInstance().addMenuItem(ApplicationMenuItemRepository.MENUITEM_ADDRESSCARD_VIEW,this);
        }

    }

    //methods we must implement

    //toString should return the string we want to
    //use as the lable of the menuItem
    public String toString(){
        return "Onto Tasks...";
    }
    
    private void readSupportedFields() {
        System.out.println("TodoList contents:");        
        try {
            String[] lists = PIM.getInstance().listPIMLists(PIM.TODO_LIST);
            int length = lists.length;
            ToDoList[] todoList = new ToDoList[length];
            for (int i = 0; i < length; i++) {
            
                todoList[i] = (ToDoList) PIM.getInstance().openPIMList(PIM.TODO_LIST, PIM.READ_WRITE, lists[i]);
                System.out.println(i+1+": "+lists[i]);
                int[] fields = todoList[i].getSupportedFields();
                System.out.println("Supported fields:");
                for (int j = 0; j < fields.length; j++) {
                        System.out.println(fields[j]+"="+todoList[i].getFieldLabel(fields[j]));
                }
            }           
        } catch (PIMException pe) {
        // no such list
        } catch (SecurityException se) {
            // MIDlet is not allowed to access to the specified list
        }
    }
    private void readAllTasks() {
        System.out.println("======\nToDo content:");        
        try {
            String[] lists = PIM.getInstance().listPIMLists(PIM.TODO_LIST);
            int length = lists.length;
            ToDoList[] todoList = new ToDoList[length];
            for (int i = 0; i < length; i++) {
            
                todoList[i] = (ToDoList) PIM.getInstance().openPIMList(PIM.TODO_LIST, PIM.READ_WRITE, lists[i]);
                System.out.println(i+1+": "+lists[i]);
                for (Enumeration e = todoList[i].items() ; e.hasMoreElements() ;) {
                    BlackBerryToDo todo = (BlackBerryToDo)e.nextElement();
                    int[] fields = todo.getFields();
                    for (int x=0;x<fields.length;x++)
                    {
                        System.out.print(fields[x]+"="+todoList[i].getFieldLabel(fields[x]));
                        if (todoList[i].getFieldDataType(fields[x]) == PIMItem.DATE) {
                            System.out.println("=" + todo.getDate(fields[x],0));
                        } else if (todoList[i].getFieldDataType(fields[x]) == PIMItem.INT) {
                            System.out.println("=" + todo.getInt(fields[x],0));
                        } else if (todoList[i].getFieldDataType(fields[x]) == PIMItem.BOOLEAN) {
                            System.out.println("=" + todo.getBoolean(fields[x],0));
                        } else if (todoList[i].getFieldDataType(fields[x]) == PIMItem.STRING) {
                            System.out.println("=" + todo.getString(fields[x],0));
                        }
                    }
                }      
            }
     
        } catch (PIMException pe) {
        // no such list
        } catch (SecurityException se) {
            // MIDlet is not allowed to access to the specified list
        }

    }
/**
 * Returns the name to be displayed for a given Contact
 *
 * @param contact The Contact for which to extract the display name
 * @return The name to be displayed in the list field
 */
public static String getDisplayName(Contact contact) {
    if (contact == null) {
        return null;    }

    String displayName = null;

    // First, see if there is a meaningful name set for the contact.
    if (contact.countValues(Contact.NAME) > 0) {
        final String[] name = contact.getStringArray(Contact.NAME, 0);
        final String firstName = name[Contact.NAME_GIVEN];
        final String lastName = name[Contact.NAME_FAMILY];
        if (firstName != null && lastName != null) {
            displayName = firstName + " " + lastName;
        } else if (firstName != null) {
            displayName = firstName;
        } else if (lastName != null) {
            displayName = lastName;
        }

        if (displayName != null) {
            final String namePrefix = name[Contact.NAME_PREFIX];
            if (namePrefix != null) {
                displayName = namePrefix + " " + displayName;
            }
            return displayName;
        }
    }

    // If not, use the company name.
    if (contact.countValues(Contact.ORG) > 0) {
        final String companyName = contact.getString(Contact.ORG, 0);
        if (companyName != null) {
            return companyName;
        }
    }
    return displayName;
    }

    //Run is called when the menuItem is invokes
    public Object run(Object context) {
                        
        ToDoList tl;
        
        BlackBerryToDo newTask = null;
        
        int doCopy = OntoCalendarOptions.getDoCopy();
        //long taskReminderMillis = OntoCalendarOptions.getTaskReminderMillis();
        int taskDueIndex = OntoCalendarOptions.getTaskDueIndex();   /* increments of 1 day */

        try {
            String msgContent;
            
            int attr =  ToDo.ATTR_NONE;
            int body = ToDo.NOTE;
            int subject = ToDo.SUMMARY;
            Date _date = new Date();
            long currentTime = _date.getTime();
            TimeZone _timeZone = TimeZone.getDefault();
            Calendar _calendar = Calendar.getInstance();
            int gmtOffset = _timeZone.getOffset(1, _calendar.get(Calendar.YEAR), _calendar.get(Calendar.MONTH), _calendar.get(Calendar.DATE), _calendar.get(Calendar.DAY_OF_WEEK), _calendar.get(Calendar.MILLISECOND));            
            long now=currentTime + gmtOffset;
            long due=now + taskDueIndex * 24 * 60 * 60 * 1000;
                
            tl = (ToDoList)PIM.getInstance().openPIMList(PIM.TODO_LIST, PIM.READ_WRITE);
            newTask = (BlackBerryToDo)tl.createToDo();

            if (tl.isSupportedField(ToDo.DUE) && taskDueIndex > 0) {
                newTask.addDate(ToDo.DUE, ToDo.ATTR_NONE, due);
            }
            
            //readSupportedFields();
            // readAllTasks();          

            //if (tl.isSupportedField(BlackBerryToDo.REMINDER) && taskReminderMillis > 0) {
            //    newTask.addDate(BlackBerryToDo.REMINDER, BlackBerryToDo.ATTR_NONE, taskReminderMillis);
            //}
  
            if (null != context) {
                System.out.println("Onto Tasks..." + context.getClass().getName());
            } else {
                System.out.println("onto Tasks...null context");
            }
            if (context instanceof TextMessage) {
                TextMessage message = (TextMessage) context;

                msgContent = message.getPayloadText();
                if (null != msgContent) {
                    newTask.addString(subject, attr, msgContent);
                }            
                if (doCopy > 0) {
                    msgContent = message.getPayloadText();
                    if (null != msgContent) {
                            newTask.addString(body, attr, msgContent);
                    }
                }
            } else if (context instanceof net.rim.blackberry.api.mail.Message) {
                net.rim.blackberry.api.mail.Message message = (net.rim.blackberry.api.mail.Message) context;
                msgContent = message.getSubject();
                if (null != msgContent) {
                    newTask.addString(subject, attr, msgContent);
                }            
                if (doCopy > 0) {
                    String msgBody = findEmailBody(message.getContent());
                    if (null != msgBody)
                        newTask.addString(body, attr, msgBody);
                    else
                        return null;
                }
            } else if (context instanceof net.rim.blackberry.api.phone.phonelogs.PhoneCallLog) {
                net.rim.blackberry.api.phone.phonelogs.PhoneCallLog call = (net.rim.blackberry.api.phone.phonelogs.PhoneCallLog)context;
                String name = call.getParticipant().getName();
                if (null == name) name = "";
                String num = call.getParticipant().getAddressBookFormattedNumber();
                
                if ((call.getType() == net.rim.blackberry.api.phone.phonelogs.PhoneCallLog.TYPE_MISSED_CALL_OPENED) 
                    || (call.getType() == net.rim.blackberry.api.phone.phonelogs.PhoneCallLog.TYPE_MISSED_CALL_UNOPENED)) {
                    msgContent = "Return call from " + name + " (" + num +")";
                } else {
                    msgContent = "Contact " + name + " (" + num + ")";
                }
                if (null != msgContent) {
                    newTask.addString(subject,attr, msgContent);
                }
                Date date = call.getDate();
                if (date == null)
                    msgContent = "";
                else
                    msgContent = date.toString();
                String notes = call.getNotes();
                if (null != notes) {
                    msgContent = msgContent + "\n\r" + notes;
                }
                if (null != msgContent) {
                    newTask.addString(body,attr,msgContent);
                }
            } else if (context instanceof net.rim.blackberry.api.pdap.BlackBerryContact) {
                net.rim.blackberry.api.pdap.BlackBerryContact contact = (net.rim.blackberry.api.pdap.BlackBerryContact)context;
                PIM pim = PIM.getInstance();
                newTask.addString(subject,attr, "Contact " + getDisplayName(contact));
                
                String[] dataFormats = pim.supportedSerialFormats(PIM.CONTACT_LIST);

                ByteArrayOutputStream output = new ByteArrayOutputStream();
                pim.toSerialFormat(contact, output, "UTF8", dataFormats[1]);
                output.close();
                
                newTask.addString(body,attr,output.toString());
            }
        } catch (Exception ex) {
            Dialog.alert(ex.getClass().getName() + ": " + ex.getMessage());
            return null;
        }
        
        Invoke.invokeApplication(Invoke.APP_TYPE_TASKS, new TaskArguments(TaskArguments.ARG_NEW, newTask) );
        return context;
    }

    //This method reads within an email message to find the email body.
    private String findEmailBody(Object obj)
    {
        
        if(obj instanceof Multipart)
        {
            Multipart mp = (Multipart)obj;
            
            //Extract all of the parts within the Multipart message.
            for(int count=0; count < mp.getCount(); ++count)
            {
                String msgBody = findEmailBody(mp.getBodyPart(count));
                if (null != msgBody)
                    return msgBody;
            }
        }
        else if (obj instanceof TextBodyPart)
        {
            //This message only has a text body.
            TextBodyPart tbp = (TextBodyPart)obj;
            return readEmailBody(tbp);
        } else if (obj instanceof BodyPart) {
            BodyPart bp = (BodyPart)obj;

            if (bp.getContentType().indexOf("text/html") != -1)
            {
                //The message has no attachments.
                //Read the email body, which may contain a TexBodyPart, MimeBodyPart or both.
                return readEmailBody(bp);
            }
            else

               if (bp.getContentType().equals("multipart/mixed") || 
                    bp.getContentType().equals("multipart/alternative"))
            {
                //The message has attachments or we are at the top level of the message.  Dig deeper to find the body.
                //Extract all of the parts within the MimeBodyPart message.
                return findEmailBody(bp.getContent());
            }
            System.out.println("findEmailBody: unknown MIME type :" + bp.getContentType());
            return null;
        }
        System.out.println("findEmailBody: unknown object instance: " + obj.toString());
        return null;
    }
        
        
    private String readEmailBody(TextBodyPart tbp)
    {
        //This is the plain text body.
        String body = (String)tbp.getContent();
        
        //Determine if all of the text body part is present.
        if (tbp.hasMore() && !tbp.moreRequestSent())
        {
            //It does, request more of the message.
            try
            {
                Transport.more((BodyPart)tbp, true);
                Status.show("Requesting more of the plain text message body.  Reopen the screen to view it once more has been received.");
                return null;
            }
            catch (Exception ex)
            {
                Dialog.alert("Exception: " + ex.toString());
                return null;
            }
        }
        return body;
    }
   
    //Displays the HTML or plain text body of the email message.
    private String readEmailBody(BodyPart bp)
    {
        //Extract the content of the message.
        Object obj = bp.getContent();
        String mimeType = bp.getContentType();
        String body = null;
        
        //Determine if the data returned is a String or a byte array.
        //If the BlackBerry is able to convert the HTML content into a String,
        //then a String should be returned.  If the encoding is not supported a 
        //byte array is returned to allow your application to work with the raw data.
        if (obj instanceof String)
        {
            body = (String)obj;
        }
        else if (obj instanceof byte[])
        {
            body = new String((byte[])obj);
        }
        
        if (mimeType.indexOf(BodyPart.ContentType.TYPE_TEXT_PLAIN_STRING) != -1)
        {
            //This is the plain text body.
            
            //Determine if all of the text body part is present.
            if (bp.hasMore() && !bp.moreRequestSent())
            {
                //It does, request more of the message.
                try
                {
                    Transport.more((BodyPart)bp, true);
                    Status.show("Requesting more of the plain text message body.  Reopen the screen to view it once more has been received.");
                    return null;
                }
                catch (Exception ex)
                {
                    Dialog.alert("Exception: " + ex.toString());
                    return null;
                }
            }
            return body;
        }
        else if (mimeType.indexOf("text/html") != -1)
        {
            //Determine if all of the HTML body part is present.
            if (bp.hasMore() && !bp.moreRequestSent())
            {
                //It does, request more of the message.
                try
                {
                    Transport.more((BodyPart)bp, true);
                    Status.show("Requesting more of the HTML message body.  Reopen the screen to view it once more has been received.");
                    return null;
                }
                catch (Exception ex)
                {
                    Dialog.alert("Exception: " + ex.toString());
                    return null;
                }
            }
            return body;
        }
        
        System.out.println("readEmailBody: unknown mime type: " + mimeType);        
        return null;
    }
}


// based on http://tinyurl.com/5a82jf

class Listener implements FolderListener,net.rim.blackberry.api.mail.event.MessageListener {

    private Store store;

    public Listener() {
        
        try {
            Session session = Session.waitForDefaultSession();
            store = session.getStore();
            store.addFolderListener(this);
        } catch (Exception e) {
            // TODO: Errorhandling
        }
    }

    public void destroyInboxListener() {
        if (store != null) {
            store.removeFolderListener(this);
        }
    }

    private void processEvent(net.rim.blackberry.api.mail.Message message) {
        Object content = ((Part) message).getContent();
        if (content instanceof BodyPart) {
            BodyPart bodyPart = (BodyPart) content;
            if (bodyPart.hasMore()) {
                // message has more parts, wait for them

                if (!bodyPart.moreRequestSent()) {
                    // no request for other parts yet

                    // add listener to this message
                    ((net.rim.blackberry.api.mail.Message) message).addMessageListener((net.rim.blackberry.api.mail.event.MessageListener) this);
                    
                    // request other parts
                    try {
                        Transport.more(bodyPart, true);
                    } catch (Exception e) {
                    }
                }
            } else {
                // message complete, remove listener
                ((net.rim.blackberry.api.mail.Message) message).removeMessageListener(this);
                
                String completeContent = ((net.rim.blackberry.api.mail.Message) message).getBodyText();

                //process content here

                //delete the message
                try {
                    Folder folder = ((net.rim.blackberry.api.mail.Message) message).getFolder();
                    folder.deleteMessage((net.rim.blackberry.api.mail.Message) message);
                } catch (Exception e) {
                    // TODO: Errorhandling
                }
            }
        }
    }

        public void messagesAdded(FolderEvent e) {
                // TODO Auto-generated method stub
                
        }

        public void messagesRemoved(FolderEvent e) {
                // TODO Auto-generated method stub
                
        }

        public void changed(MessageEvent e) {
                // TODO Auto-generated method stub
                
        }


} 

